Using Modal Dialog Boxes
Remember that a modal dialog box puts the user into the state or "mode" of being able to work only inside the dialog box. The user cannot move the dialog box and can dismiss it only by clicking its buttons (perhaps after supplying some necessary information).
In general, it's easier to create and handle simple modal dialog boxes than it is to create and handle modeless dialog boxes. The reason is that the Dialog Manager provides special routines that you can call to display alerts and other simple dialog boxes. The Dialog Manager also provides the
- Note
- The Dialog Manager also provides movable modal dialog boxes; these are modal dialog boxes that contain a title bar so that the user can drag the dialog box. You should use movable modal dialog boxes whenever the user might need to move a modal dialog box to see what it obscures or whenever you want allow the user to switch to another application while the dialog box is displayed.
![]()
ModalDialog
procedure, which you can call to manage all user actions in modal dialog boxes.
- IMPORTANT
- Ease of implementation is not a sufficient reason for using modal dialog boxes instead of modeless ones. You should avoid using modal dialog boxes except when absolutely necessary.
![]()
Displaying a Modal Dialog Box
Listing 7-7 shows a standard way to display a modal dialog box. It defines the procedureDoAboutBox
, which is called after the user chooses the About Venn Diagrammer command from the Apple menu.Listing 7-7 Displaying a modal dialog box
PROCEDURE DoAboutBox (myWindow: WindowPtr); VAR myWindow: WindowPtr; myDialog: DialogPtr; myItem: Integer; BEGIN myWindow := FrontWindow; IF myWindow <> NIL THEN DoActivate(myWindow, 1 - activeFlag); myDialog := GetNewDialog(rAboutDial, NIL, WindowPtr(-1)); IF myDialog <> NIL THEN BEGIN SetPort(myDialog); DoDefaultButton(myDialog); REPEAT ModalDialog(@MyModalFilter, myItem); UNTIL myItem = iOK; DisposeDialog(myDialog); SetPort(myWindow); END; END;When you display a modal dialog box, you should first deactivate any existing front window. TheDoAboutBox
procedure retrieves a window pointer to the front window and passes that pointer to the application-defined activate routineDoActivate
. ThenDoAboutBox
callsGetNewDialog
to open the dialog box specified by the resource IDrAboutDial
:
CONST rAboutDial = 7000; {resource ID of About dialog}IfGetNewDialog
returns a dialog pointer whose value is notNIL
, thenDoAboutBox
callsSetPort
to establish the new dialog box as the current drawing port. Then it calls the application-defined procedureDoDefaultButton
(defined in Listing 7-8) to draw a thick border around the default button. This indicates that the user can dismiss the dialog box by pressing the Return key or the Enter key.Listing 7-8 Outlining the default button of a modal dialog box
PROCEDURE DoDefaultButton (myDialog: DialogPtr); VAR myType: Integer; myHand: Handle; myRect: Rect; BEGIN GetDialogItem(myDialog, iOK, myType, myHand, myRect); DoOutlineControl(myHand); END;TheDoDefaultButton
procedure simply calls the application-defined procedureDoOutlineControl
to outline the dialog item whose item number is 1 (identified by the constantiOK
). See page 200 for a definition ofDoOutlineControl
.At this point, the modal dialog box is displayed on the screen. The
DoAboutBox
procedure loops indefinitely, repeatedly callingModalDialog
until the user clicks the OK button. TheModalDialog
procedure handles all mouse, keystroke, and update events that occur inside the dialog box until an event involving an enabled dialog item occurs. When that happens,ModalDialog
exits and returns the dialog item number in the second parameter. Your application can then do whatever is appropriate in response to an event in that item. InDoAboutBox
,ModalDialog
is called repeatedly until a click in the OK button occurs. At that time, the modal dialog is removed from the screen, andDoAboutBox
callsSetPort
to reinstate the original drawing port.Defining a Modal Dialog Filter Function
The actions ofModalDialog
are guided by the modal dialog filter function whose address is passed in its first parameter. If you passNIL
as the first parameter to theModalDialog
procedure, you'll get the standard event filtering provided by the Dialog Manager. The standard event filter function returnsTRUE
and causesModalDialog
to return item number 1 (the number of the default button) when the user presses the Return or the Enter key.For most modal dialog boxes, the standard modal dialog filter function is too simple. Your application should define a modal dialog filter function that performs the following tasks:
Listing 7-9 defines a modal dialog filter function that accomplishes these tasks. In addition, the filter function
- return
TRUE
and the item number for the default button if the user presses the Return key or the Enter key- return
TRUE
and the item number for the Cancel button if the user presses the Escape key or the Command-period combination- allow background applications to receive update events and return
FALSE
when they do- return
FALSE
for all other events that your event filter doesn't handle
MyModalFilter
handles any disk-inserted events that occur while the modal dialog box is displayed.Listing 7-9 A modal dialog filter function
FUNCTION MyModalFilter (myDialog: DialogPtr; VAR myEvent: EventRecord; VAR myItem: Integer): Boolean; VAR myType: Integer; myHand: Handle; myRect: Rect; myKey: Char; myIgnore: LongInt; BEGIN MyModalFilter := FALSE; {assume we don't handle the event} CASE myEvent.what OF updateEvt: BEGIN IF WindowPtr(myEvent.message) <> myDialog THEN DoUpdate(WindowPtr(myEvent.message)); {update the window behind} END; keyDown, autoKey: BEGIN myKey := char(BAnd(myEvent.message, charCodeMask)); {if Return or Enter pressed, do default button} IF (myKey = kReturn) OR (myKey = kEnter) THEN BEGIN GetDialogItem(myDialog, iOK, myType, myHand, myRect); HiliteControl(ControlHandle(myHand), 1); {make button appear to have been pressed} Delay(kVisualDelay, myIgnore); HiliteControl(ControlHandle(myHand), 0); MyModalFilter := TRUE; myItem := iOK; END; {if Escape or Cmd-. pressed, do Cancel button} IF (myKey = kEscape) OR ((myKey = kPeriod) AND (BAnd(myEvent.modifiers, CmdKey) <> 0)) THEN BEGIN GetDialogItem(myDialog, iCancel, myType, myHand, myRect); HiliteControl(ControlHandle(myHand), 1); {make button appear to have been pressed} Delay(kVisualDelay, myIgnore); HiliteControl(ControlHandle(myHand), 0); MyModalFilter := TRUE; myItem := iCancel; END; END; diskEvt: BEGIN DoDiskEvent(myEvent); MyModalFilter := TRUE; {show we've handled the event} END; OTHERWISE ; END; {CASE} END;An interesting part ofMyModalFilter
is the way it intercepts key-down events and translates them into button clicks. When, for instance, it detects that the Return key was pressed, it callsGetDialogItem
to retrieve a handle to the first item in the item list (by convention, the OK button). ThenMyModalFilter
callsHiliteControl
to invert the state of the button, waits for a specified number of ticks, and then callsHiliteControl
once again to restore the button to its original state. Finally, it sets the function result and the variable parametermyItem
, thus informing the calling routine that the event was handled.